/**
 * create by zhike
 * date:2015年3月20日
 */
package com.acooly.module.openapi.client.provider.baofup;

import com.acooly.core.utils.Strings;
import com.acooly.module.openapi.client.api.enums.ApiMessageType;
import com.acooly.module.openapi.client.api.message.ApiMessage;
import com.acooly.module.openapi.client.api.message.MessageFactory;
import com.acooly.module.openapi.client.api.message.MessageMeta;
import com.acooly.module.openapi.client.provider.baofup.domain.BaoFuPApiMsgInfo;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Service;

import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Map;

/**
 * @author zhike
 */
@Service
public class BaoFuPMessageFactory implements MessageFactory, InitializingBean {

    private Logger logger = LoggerFactory.getLogger(BaoFuPMessageFactory.class);

    public Map<String, MessageMeta> metas = Maps.newHashMap();

    private String messagePackage = "classpath*:com.acooly.module.openapi.client.provider.baofup.message";

    @Override
    public ApiMessage getRequest(String serviceName) {
        return newInstance(metas.get(serviceName).getRequest());
    }

    @Override
    public ApiMessage getResponse(String serviceName) {
        return newInstance(metas.get(serviceName).getResponse());
    }

    @Override
    public ApiMessage getNotify(String serviceName) {
        return newInstance(metas.get(serviceName).getAsyncNotify());
    }

    @Override
    public ApiMessage getReturn(String serviceName) {
        return newInstance(metas.get(serviceName).getSyncNotify());
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }


    @SuppressWarnings("unchecked")
    public void init() {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            Resource[] resources = resourcePatternResolver.getResources(getResourcePattern());
            ClassLoader loader = this.getClass().getClassLoader();
            String binaryName = null;
            for (Resource resource : resources) {
                binaryName = getCanonicalClassPath(resource);
                try {
                    Class<?> clazz = loader.loadClass(binaryName);
                    if (Modifier.isAbstract(clazz.getModifiers())) {
                        continue;
                    }
                    BaoFuPApiMsgInfo baoFuApiMsgInfo = getAnotation(clazz);
                    if (baoFuApiMsgInfo != null) {
                        register(baoFuApiMsgInfo, (Class<? extends ApiMessage>) clazz);
                    }
                } catch (Exception e) {
                    continue;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("扫描Message失败:" + e.getMessage());
        }
    }


    protected void register(BaoFuPApiMsgInfo apiMsg, Class<? extends ApiMessage> clazz) {
        logger.info("注册Message:{}", apiMsg);
        String serviceKey = apiMsg.service().getKey();
        if (metas.get(serviceKey) == null) {
            metas.put(serviceKey, new MessageMeta());
        }
        if (apiMsg.type() == ApiMessageType.Request) {
            metas.get(serviceKey).setRequest(clazz);
        } else if (apiMsg.type() == ApiMessageType.Response) {
            metas.get(serviceKey).setResponse(clazz);
        } else if (apiMsg.type() == ApiMessageType.Return) {
            metas.get(serviceKey).setSyncNotify(clazz);
        } else if (apiMsg.type() == ApiMessageType.Notify) {
            metas.get(serviceKey).setAsyncNotify(clazz);
        }
    }


    protected BaoFuPApiMsgInfo getAnotation(Class<?> clazz) {
        return clazz.getAnnotation(BaoFuPApiMsgInfo.class);
    }

    protected String getCanonicalClassPath(Resource resource) {
        try {
            URL classFileUrl = resource.getURL();
            String path = classFileUrl.getPath();
            if (Strings.contains(path, "classes/")) {
                path = StringUtils.substringAfter(path, "classes/");
            }
            if (Strings.contains(path, "jar!/")) {
                path = StringUtils.substringAfterLast(path, "jar!/");
            }
            path = path.replaceAll("/", ".").replace(".class", "");
            return path;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    private String getResourcePattern() {
        return Strings.replace(messagePackage, ".", "/") + "/**/*.class";
    }

    private <T> T newInstance(Class<T> clazz) {
        try {
            return (T) clazz.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("InstantiationException:" + clazz);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("IllegalAccessException:" + clazz);
        }
    }

    public void setMessagePackage(String messagePackage) {
        this.messagePackage = messagePackage;
    }
}
